/*
 * CircuitPanel.cpp
 *
 * Copyright 2012 Pieter Agten
 *
 * This file is part of tltgen.
 *
 * tltgen is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * tltgen is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with tltgen.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @file   CircuitPanel.cpp
 * @author Pieter Agten (pieter.agten@gmail.com)
 * @date   27 jan 2013
 */

#include <wx/wx.h>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>

#include "Resistance.hpp"
#include "Voltage.hpp"
#include "AdcValue.hpp"
#include "AnalogToDigitalConverter.hpp"
#include "LinearAdc.hpp"
#include "Circuit.hpp"
#include "VoltageDividerCircuit.hpp"

#include "CircuitPanel.hpp"

using boost::shared_ptr;
using boost::make_shared;

namespace tltgen {

  BEGIN_EVENT_TABLE(CircuitPanel, wxPanel)
  EVT_CHECKBOX(ID_UseParallelResistor, CircuitPanel::onUseParallelResistorChanged)
  END_EVENT_TABLE()

  CircuitPanel::CircuitPanel(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, 
			     long style, const wxString& name) :
    wxPanel(parent, id, pos, size, style, name)
  {
    wxSizer* vbox = new wxBoxSizer(wxVERTICAL);

    wxSizer* gs = new wxGridSizer(6, 2, 3, 3);
    gs->Add(new wxStaticText(this, wxID_ANY, wxT("Nb. ADC bits")), 0, wxALIGN_CENTER_VERTICAL);
    this->txtNbAdcBits = new wxTextCtrl(this, wxID_ANY, formatNbAdcBits(10));
    gs->Add(this->txtNbAdcBits, 0, wxEXPAND | wxLEFT, 2);

    gs->Add(new wxStaticText(this, wxID_ANY, wxT("ADC reference voltage")), 0, wxALIGN_CENTER_VERTICAL);
    this->txtAdcReferenceVoltage = new wxTextCtrl(this, wxID_ANY, formatVoltage(5));
    gs->Add(this->txtAdcReferenceVoltage, 0, wxEXPAND | wxLEFT, 2);


    gs->Add(new wxStaticText(this, wxID_ANY, wxT("Circuit voltage")), 0, wxALIGN_CENTER_VERTICAL);
    this->txtCircuitInputVoltage = new wxTextCtrl(this, wxID_ANY, formatVoltage(5));
    gs->Add(this->txtCircuitInputVoltage, 0, wxEXPAND | wxLEFT, 2);

    gs->Add(new wxStaticText(this, wxID_ANY, wxT("Divider resistance")), 0, wxALIGN_CENTER_VERTICAL);
    this->txtDividerResistance = new wxTextCtrl(this, wxID_ANY, formatResistance(4700));
    gs->Add(this->txtDividerResistance, 0, wxEXPAND | wxLEFT, 2);

    this->chkUseParallelResistor = new wxCheckBox(this, ID_UseParallelResistor, wxT("Use parallel resistor"));
    this->chkUseParallelResistor->SetValue(false);
    gs->Add(this->chkUseParallelResistor);
    gs->Add(new wxStaticText(this, wxID_ANY, wxEmptyString));

    gs->Add(new wxStaticText(this, wxID_ANY, wxT("Parallel resistance")), 0, wxALIGN_CENTER_VERTICAL);
    this->txtParallelResistance = new wxTextCtrl(this, wxID_ANY, formatResistance(680));
    gs->Add(this->txtParallelResistance, 0, wxEXPAND | wxLEFT, 2);

    vbox->Add(gs);

    updateParallelResistanceEnabled();
    SetSizer(vbox);
  }

  CircuitPanel::~CircuitPanel()
  { }

  unsigned int CircuitPanel::getNbAdcBits() const
  {
    return getValueAsUnsignedInt(this->txtNbAdcBits->GetValue());
  }
  void CircuitPanel::setNbAdcBits(unsigned int value)
  {
    this->txtNbAdcBits->SetValue(formatNbAdcBits(value));
  }
  Voltage CircuitPanel::getAdcReferenceVoltage() const
  {
    return getValueAsDouble(this->txtAdcReferenceVoltage->GetValue());
  }
  void CircuitPanel::setAdcReferenceVoltage(Voltage value)
  {
    this->txtAdcReferenceVoltage->SetValue(formatVoltage(value));
  }
  void CircuitPanel::setAdcValues(const shared_ptr<AnalogToDigitalConverter const>& adc)
  {
    adc->accept(this);
  }
  void CircuitPanel::visit(const LinearAdc* adc)
  {
    this->txtNbAdcBits->SetValue(formatNbAdcBits(adc->getNbBits()));
    this->txtAdcReferenceVoltage->SetValue(formatVoltage(adc->getReferenceVoltage()));
  }

  Voltage CircuitPanel::getCircuitInputVoltage() const
  {
    return getValueAsDouble(this->txtCircuitInputVoltage->GetValue());
  }
  void CircuitPanel::setCircuitInputVoltage(Voltage value)
  {
    this->txtCircuitInputVoltage->SetValue(formatVoltage(value));
  }
  Resistance CircuitPanel::getDividerResistance() const
  {
    return getValueAsDouble(this->txtDividerResistance->GetValue());
  }
  void CircuitPanel::setDividerResistance(Resistance value)
  {
    this->txtDividerResistance->SetValue(formatResistance(value));
  }
  bool CircuitPanel::getUseParallelResistor() const
  {
    return this->chkUseParallelResistor->GetValue();
  }
  void CircuitPanel::setUseParallelResistor(bool value)
  {
    this->chkUseParallelResistor->SetValue(value);
  }
  Resistance CircuitPanel::getParallelResistance() const
  {
    return getValueAsDouble(this->txtParallelResistance->GetValue());
  }
  void CircuitPanel::setParallelResistance(Resistance value)
  {
    this->txtParallelResistance->SetValue(formatResistance(value));
  }
  void CircuitPanel::setCircuitValues(const shared_ptr<Circuit const>& circuit)
  {
    circuit->accept(this);
  }
  void CircuitPanel::visit(const VoltageDividerCircuit* circuit)
  {
    this->txtCircuitInputVoltage->SetValue(formatVoltage(circuit->getInputVoltage()));
    this->txtDividerResistance->SetValue(formatResistance(circuit->getDividerResistance()));
    this->chkUseParallelResistor->SetValue(circuit->hasParallelResistor());
    this->txtParallelResistance->SetValue(formatResistance(circuit->getParallelResistance()));
    updateParallelResistanceEnabled();
  }
  
  shared_ptr<AnalogToDigitalConverter> CircuitPanel::createAnalogToDigitalConverter() const
  {
    return make_shared<LinearAdc>(getNbAdcBits(), getAdcReferenceVoltage());
  }
  shared_ptr<Circuit> CircuitPanel::createCircuit() const
  {
    if (getUseParallelResistor()) {
      return make_shared<VoltageDividerCircuit>(getCircuitInputVoltage(), getDividerResistance(), getParallelResistance());
    } else {
      return make_shared<VoltageDividerCircuit>(getCircuitInputVoltage(), getDividerResistance());
    }
  }

  void CircuitPanel::updateParallelResistanceEnabled()
  {
    this->txtParallelResistance->Enable(this->chkUseParallelResistor->IsChecked());
  }

  void CircuitPanel::onUseParallelResistorChanged(wxCommandEvent& event)
  {
    updateParallelResistanceEnabled();
  }

  wxString CircuitPanel::formatNbAdcBits(unsigned int value) const
  {
    return wxString::Format(wxT("%d"), value);
  }

  wxString CircuitPanel::formatVoltage(Voltage value) const
  {
    return wxString::Format(wxT("%.2f"), value);
  }

  wxString CircuitPanel::formatResistance(Resistance value) const
  {
    return wxString::Format(wxT("%.0f"), value);
  }

  unsigned int CircuitPanel::getValueAsUnsignedInt(const wxString& string) const
  {
    long value = 0;
    string.ToLong(&value);
    return (int)value;
  }

  double CircuitPanel::getValueAsDouble(const wxString& string) const
  {
    double value = NAN;
    string.ToDouble(&value);
    return value;
  }

}

